
package com.usc.nearbyus;


import java.util.List;

import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.Log;
 
/**
 * Android Accelerometer Sensor Manager Archetype
 * @author antoine vianey
 * under GPL v3 : http://www.gnu.org/licenses/gpl-3.0.html
 */
public class AccelerometerManager {
 
	private final static String DEBUGTAG = "AccelerometerManager";
	
    /** Accuracy configuration */
    private static double threshold     = 0.000000005; 
    private static int interval     = 1000;
    private static int intervalMax     = 1000000000;    // i.e., if it takes longer then this, ignore; half a second = 500000000
    private static boolean limitOneTriggerPerInterval = true;  // i.e., 
 
    private static Sensor sensor;
    private static SensorManager sensorManager;
    // you could use an OrientationListener array instead
    // if you plans to use more than one listener
    private static AccelerometerListener listener;
 
    /** indicates whether or not Accelerometer Sensor is supported */
    private static Boolean supported;
    /** indicates whether or not Accelerometer Sensor is running */
    private static boolean running = false;
 
    /**
     * Returns true if the manager is listening to orientation changes
     */
    public static boolean isListening() {
        return running;
    }
 
    /**
     * Unregisters listeners
     */
    public static void stopListening() {
        running = false;
        try {
            if (sensorManager != null && sensorEventListener != null) {
                sensorManager.unregisterListener(sensorEventListener);
            }
        } catch (Exception e) {}
    }
 
    /**
     * Returns true if at least one Accelerometer sensor is available
     */
    public static boolean isSupported() {
        if (supported == null) {
            if (Nearbyus.getContext() != null) {
                sensorManager = (SensorManager) Nearbyus.getContext().
                        getSystemService(Context.SENSOR_SERVICE);
                List<Sensor> sensors = sensorManager.getSensorList(
                        Sensor.TYPE_ACCELEROMETER);
                supported = new Boolean(sensors.size() > 0);
            } else {
                supported = Boolean.FALSE;
            }
        }
        return supported;
    }
 
    /**
     * Configure the listener for shaking
     * @param threshold
     *             minimum acceleration variation for considering shaking
     * @param interval
     *             minimum interval between to shake events
     */
    public static void configure(int threshold, int interval) {
        AccelerometerManager.threshold = threshold;
        AccelerometerManager.interval = interval;
    }
 
    /**
     * Registers a listener and start listening
     * @param accelerometerListener
     *             callback for accelerometer events
     */
    public static void startListening(AccelerometerListener accelerometerListener) {
        sensorManager = (SensorManager) Nearbyus.getContext().
                getSystemService(Context.SENSOR_SERVICE);
        List<Sensor> sensors = sensorManager.getSensorList(
                Sensor.TYPE_ACCELEROMETER);
        if (sensors.size() > 0) {
            sensor = sensors.get(0);
            running = sensorManager.registerListener(
                    sensorEventListener, sensor, 
                    SensorManager.SENSOR_DELAY_NORMAL);     // to save power, no need for SENSOR_DELAY_GAME says bgs on march 14
            listener = accelerometerListener;
        }
    }
 
    /**
     * Configures threshold and interval
     * And registers a listener and start listening
     * @param accelerometerListener
     *             callback for accelerometer events
     * @param threshold
     *             minimum acceleration variation for considering shaking
     * @param interval
     *             minimum interval between to shake events
     */
    public static void startListening(
            AccelerometerListener accelerometerListener, 
            int threshold, int interval) {
        configure(threshold, interval);
        startListening(accelerometerListener);
    }
 
    /**
     * The listener that listen to events from the accelerometer listener
     */
    private static SensorEventListener sensorEventListener = 
        new SensorEventListener() {
 
        private long now = 0;
        private long timeDiff = 0;
        private long lastUpdate = 0;
        private long lastShake = 0;
 
        private float x = 0;
        private float y = 0;
        private float z = 0;
        private float lastX = 0;
        private float lastY = 0;
        private float lastZ = 0;
        private float force = 0;
        
        private float forceMaxThisPeriod=0;
        private long periodStart=0;
        private boolean okToTriggerThisPeriod=true;
 
        public void onAccuracyChanged(Sensor sensor, int accuracy) {}
 
        public void onSensorChanged(SensorEvent event) {
            // use the event timestamp as reference
            // so the manager precision won't depend 
            // on the AccelerometerListener implementation
            // processing time
            now = event.timestamp;
        	timeDiff = now - lastUpdate; 

            if ((now-periodStart)>intervalMax) {  
//            	Log.d(DEBUGTAG , "...periodTimer; forceMaxThisPeriod="+forceMaxThisPeriod);

            	// reset our timer...
            	periodStart=now;
            	forceMaxThisPeriod=0;
            	if (limitOneTriggerPerInterval==true)
            		okToTriggerThisPeriod=true;
            }
            
         
            x = event.values[0];
            y = event.values[1];
            z = event.values[2];
 
            // if not interesting in shake events
            // just remove the whole if then else bloc
            if (lastUpdate == 0) {
                lastUpdate = now;
                lastShake = now;
                lastX = x;
                lastY = y;
                lastZ = z;
            } else {
                if (timeDiff > 0) {
                    force = ( (Math.abs(x-lastX) + Math.abs(y-lastY) + Math.abs(z-lastZ)))  / timeDiff;
                    if (force>forceMaxThisPeriod)
                    	forceMaxThisPeriod=force;
//                    Log.d(DEBUGTAG , "onSensorChange force="+force);
                    if (force > threshold) {
                    	
//                    	Log.d(DEBUGTAG , "passed the force test!");
                    	if (okToTriggerThisPeriod) {
//                        	Log.d(DEBUGTAG , "okToTriggerThisPeriod");
                        	if (now - lastShake >= intervalMax)
                        		Log.d(DEBUGTAG , "it's been too long between shakes...");
                        	else if (now - lastShake >= interval) {
                                // trigger shake event
//                                Log.d(DEBUGTAG , "we're SHAKIN!");
                                if (limitOneTriggerPerInterval==true) 
                                	okToTriggerThisPeriod=false;
                                listener.onShake(force);                   		
                            }
                        }
                    	else {
//                        	Log.d(DEBUGTAG , "not triggering (already triggered too many times this period...)");
                    	}
                        lastShake = now;
                    }
                    lastX = x;
                    lastY = y;
                    lastZ = z;
                    lastUpdate = now;
                }
            }
            // trigger change event
            listener.onAccelerationChanged(x, y, z);
        }
        
	 };
}

